/*
 * GDevelop C++ Platform
 * Copyright 2008-2016 Florian Rival (Florian.Rival@gmail.com). All rights
 * reserved. This project is released under the MIT License.
 */
#ifndef RUNTIMELAYER_H
#define RUNTIMELAYER_H
#include <SFML/Graphics.hpp>
#include "GDCpp/Runtime/String.h"
namespace gd {
class Camera;
}
namespace gd {
class Layer;
}
class RuntimeScene;

/**
 * \brief A camera which is displayed on a part of a window ( see Viewport
 * related methods ) rendering an area of the scene.
 *
 * \ingroup GameEngine
 */
class GD_API RuntimeCamera {
 public:
  /**
   * Construct a runtime camera from a nothing.
   *
   * \warning This constructor should not be used: See the two other
   * alternatives.
   */
  RuntimeCamera()
      : originalWidth(0), originalHeight(0), angle(0), zoomFactor(1){};

  /**
   * Construct a runtime camera from a sf::View ( The SFML equivalent of a
   * camera ).
   */
  RuntimeCamera(sf::View& view);

  /**
   * Construct a runtime camera from a Camera and a default view.
   */
  RuntimeCamera(gd::Camera& camera, const sf::View& defaultView);

  /**
   * Provide a read only access to the sf::View associated with the camera.
   */
  const sf::View& GetSFMLView() const { return sfmlView; };

  /**
   * Change the zoom of the camera.
   */
  void SetZoom(float newZoom);

  /**
   * Get the zoom of the camera
   */
  float GetZoom() const { return zoomFactor; };

  /**
   * Set the rotation of the camera.
   * \param newAngle the new angle of the camera, in radians.
   */
  void SetRotation(float newAngle);

  /**
   * Get the rotation of the camera
   */
  float GetRotation() const { return angle; };

  /**
   * Change the position of the view on the scene.
   */
  void SetViewCenter(const sf::Vector2f& newCenter);

  /**
   * Get the position of the view on the scene.
   */
  const sf::Vector2f& GetViewCenter() const { return sfmlView.getCenter(); };

  /**
   * Change the size of the rendered area of the scene.
   * Zoom is reset to 1.
   */
  void SetSize(float width, float height);

  /**
   * Get the width of the rendered area
   */
  float GetWidth() const {
    return zoomFactor != 0 ? originalWidth * 1.0 / zoomFactor : 0;
  };

  /**
   * Get the height of the rendered area
   */
  float GetHeight() const {
    return zoomFactor != 0 ? originalHeight * 1.0 / zoomFactor : 0;
  };

  /**
   * Change the position of the view on the scene.
   */
  void SetViewport(float x1, float y1, float x2, float y2);

 private:
  float originalWidth;
  float originalHeight;
  float angle;       ///< Angle of the camera
  float zoomFactor;  ///< Zoom factor of the camera

  sf::View
      sfmlView;  ///< The sf::View which is the SFML equivalent of a camera.
};

/**
 * \brief A layer of a layout, used to display objects using RuntimeCamera.
 *
 * \ingroup GameEngine
 */
class GD_API RuntimeLayer {
 public:
  RuntimeLayer() : isVisible(true), timeScale(1){};
  RuntimeLayer(gd::Layer& layer, const sf::View& defaultView);
  virtual ~RuntimeLayer(){};

  /**
   * Change layer name
   */
  virtual void SetName(const gd::String& name_) { name = name_; }

  /**
   * Get layer name
   */
  virtual const gd::String& GetName() const { return name; }

  /**
   * Change if layer is displayed or not
   */
  virtual void SetVisibility(bool isVisible_) { isVisible = isVisible_; }

  /**
   * True if layer will be displayed.
   */
  virtual bool GetVisibility() const { return isVisible; }

  /**
   * Get cameras count.
   */
  inline std::size_t GetCameraCount() const { return cameras.size(); };

  /**
   * Return a reference to a camera
   */
  inline const RuntimeCamera& GetCamera(std::size_t n) const {
    return cameras[n];
  }

  /**
   * Return a reference to a camera
   */
  inline RuntimeCamera& GetCamera(std::size_t n) { return cameras[n]; }

  /**
   * Delete a specific camera.
   */
  inline void DeleteCamera(std::size_t n) {
    if (n < cameras.size()) cameras.erase(cameras.begin() + n);
  }

  /**
   * Add an already existing camera.
   */
  inline void AddCamera(const RuntimeCamera& camera) {
    cameras.push_back(camera);
  };

  /**
   * \brief Get the time scale of the layer
   */
  double GetTimeScale() { return timeScale; }

  /**
   * \brief Change the time scale applied on the (objects of the) layer.
   */
  void SetTimeScale(double timeScale_) {
    if (timeScale_ >= 0) timeScale = timeScale_;
  }

  /**
   * \brief Return the time elapsed since the last frame, in microseconds, for
   * the layer. This can be different than the time elapsed on the scene
   */
  signed long long GetElapsedTime(const RuntimeScene& scene) const;

 private:
  gd::String name;                     ///< The name of the layer
  bool isVisible;                      ///< True if the layer is visible
  std::vector<RuntimeCamera> cameras;  ///< The camera displayed by the layer
  double
      timeScale;  ///< Time scale that is applied on the (objects of the) layer.
};

#endif  // RUNTIMELAYER_H
